<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>实现一个简易的Promise</title>
  </head>
  <body>
    <a href="https://www.yuque.com/liaojie3/lxxhgu/xw4uop">
      <h1>对应的学习笔记</h1>
    </a>

    <script>
      const PENDING = 'pending';
      const FULFILLED = 'fulfilled';
      const REJECT = 'rejected';

      class MyPromise {
        constructor(executor) {
          this._status = PENDING;
          this._resolveCbList = [];
          this._rejectCbList = [];
          this._value = null;
          this._reason = null;

          executor(this._resolve, this._reject);
        }

        then(onFulfilled, onRejected) {
          typeof onFulfilled === 'function' ? null : (onFulfilled = (v) => v);
          typeof onRejected === 'function' ? null : (onRejected = (v) => v);

          return new MyPromise((resolve, reject) => {
            const resolveCb = (value) => {
              const result = onFulfilled(value);
              result instanceof MyPromise
                ? result.then(resolve, reject)
                : resolve(result);
            };

            const rejectCb = (reason) => {
              const result = onRejected(reason);
              result instanceof MyPromise
                ? result.then(resolve, reject)
                : reject(result);
            };

            switch (this._status) {
              case PENDING:
                this._resolveCbList.push(resolveCb);
                this._rejectCbList.push(rejectCb);
                break;
              case FULFILLED:
                resolveCb(this._value);
                break;
              default:
                rejectCb(this._reason);
            }
          });
        }

        catch(onRejected) {
          return this.then(null, onRejected);
        }

        finally(finallyFn) {
          const cb = () => finallyFn();
          return this.then(cb, cb);
        }

        _resolve = (value) => {
          const run = () => {
            if (this._status !== PENDING) {
              return;
            }

            this._status = FULFILLED;
            this._value = value;
            while (this._resolveCbList.length) {
              const cb = this._resolveCbList.shift();
              cb(value);
            }
          };

          setTimeout(run);
        };

        _reject = (reason) => {
          const run = () => {
            if (this._status !== PENDING) {
              return;
            }

            this._status = REJECT;
            this._reason = reason;
            while (this._rejectCbList.length) {
              const cb = this._rejectCbList.shift();
              cb(reason);
            }
          };

          setTimeout(run);
        };

        static resolve(value) {
          return new MyPromise((resolve) => resolve(value));
        }

        static reject(value) {
          return new MyPromise((resolve, reject) => reject(value));
        }

        static all(iterator) {
          return new MyPromise((resolve, reject) => {
            const arr = [];
            let accumulator = 0;

            for (let i = 0; i < iterator.length; i++) {
              const p =
                iterator[i] instanceof MyPromise
                  ? iterator[i]
                  : MyPromise.resolve(iterator[i]);

              p.then(
                (value) => {
                  accumulator++;
                  arr.push(value);

                  if (accumulator === iterator.length) {
                    resolve(arr);
                  }
                },
                (reason) => {
                  reject(reason);
                }
              );
            }
          });
        }

        static race(iterator) {
          return new MyPromise((resolve, reject) => {
            iterator.forEach((item) => {
              const p =
                item instanceof MyPromise ? item : MyPromise.resolve(item);

              p.then(
                (value) => {
                  resolve(value);
                },
                (reason) => {
                  reject(reason);
                }
              );
            });
          });
        }

        static allSettled(iterator) {
          return new MyPromise((resolve, reject) => {
            const result = [];
            let accumulator = 0;

            const resolveFn = () => {
              accumulator++;
              if (accumulator === iterator.length) {
                resolve(result);
              }
            };

            iterator.forEach((item) => {
              const p =
                item instanceof MyPromise ? item : MyPromise.resolve(item);

              p.then(
                (value) => {
                  result.push({
                    status: FULFILLED,
                    value,
                  });
                  resolveFn();
                },
                (reason) => {
                  result.push({
                    status: REJECT,
                    reason,
                  });
                  resolveFn();
                }
              );
            });
          });
        }
      }
    </script>
  </body>
</html>
